﻿using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;

namespace Mars.GpuInstance
{
    [BurstCompile(CompileSynchronously = true, FloatPrecision = FloatPrecision.Low, FloatMode = FloatMode.Fast)]
    public struct SimpleAnimatorFrustumCullingJob : IJob
    {
        [ReadOnly] public NativeList<float4x4> input;
        [ReadOnly] public NativeList<float4> inputCampData;
        [ReadOnly] public NativeArray<float4> cameraPlanes;
        [ReadOnly] public float3 boxCenter;
        [ReadOnly] public float3 boxExtents;
        [ReadOnly] public float2 drawRange;
        [ReadOnly] public float3 cameraPos;
        [ReadOnly] public float4x4 transformOffset;

        [ReadOnly] public bool isSkinBatch;
        [ReadOnly] public bool batchNonPropScaling;
        [ReadOnly] public NativeList<bool> inputNonPropScaling;

        public NativeArray<float4> boundVerts;
        public NativeArray<float4x4> output;
        public NativeArray<float4> campData;


        public NativeList<int> outputIndex;

        //LOD裁剪仅计算相机高度，不是相机距离
        bool IsLodCull(float3 pos)
        {
            float dis = math.distance(pos, cameraPos);
            if (dis < drawRange.x || dis >= drawRange.y)
            {
                return true;
            }

            return false;
        }

        public void Execute()
        {
            var count = input.Length;

            var hasOffset = !transformOffset.Equals(float4x4.identity);

            for (int i = 0; i < count; i++)
            {
                // nonPropScaling 字段表示当前实例正在播放的动画是否是非等比缩放的
                // 非等比缩放需要开启一个 keyword，因此分为两个 batch，需要过滤掉不符合的
                if (isSkinBatch && inputNonPropScaling[i] != batchNonPropScaling) continue;

                var instance = input[i];
                float3 pos = new float3(instance.c3.x, instance.c3.y, instance.c3.z);
                bool isCull = IsLodCull(pos);

                if (!isCull)
                {
                    // 获得实例包围盒的8个顶点
                    float3 boundMin = boxCenter - boxExtents;
                    float3 boundMax = boxCenter + boxExtents;
                    boundVerts[0] = math.mul(instance, new float4(boundMin, 1));
                    boundVerts[1] = math.mul(instance, new float4(boundMax, 1));
                    boundVerts[2] = math.mul(instance, new float4(boundMax.x, boundMax.y, boundMin.z, 1));
                    boundVerts[3] = math.mul(instance, new float4(boundMax.x, boundMin.y, boundMax.z, 1));
                    boundVerts[6] = math.mul(instance, new float4(boundMax.x, boundMin.y, boundMin.z, 1));
                    boundVerts[4] = math.mul(instance, new float4(boundMin.x, boundMax.y, boundMax.z, 1));
                    boundVerts[5] = math.mul(instance, new float4(boundMin.x, boundMax.y, boundMin.z, 1));
                    boundVerts[7] = math.mul(instance, new float4(boundMin.x, boundMin.y, boundMax.z, 1));
                    isCull = CullingUtil.IsCulled(boundVerts, cameraPlanes);
                }

                if (!isCull) //没有被裁剪掉
                {
                    if (hasOffset)
                    {
                        output[outputIndex.Length] = math.mul(instance, transformOffset);
                    }
                    else
                    {
                        output[outputIndex.Length] = instance;
                    }
                    campData[outputIndex.Length] = inputCampData[i];
                    outputIndex.Add(i);
                }
            }
        }
    }
}